import os
import json
import requests
from pathlib import Path
from typing import List, Dict, Optional, Tuple


class OSSUploader:
    def __init__(self, config_path: str = "oss_config.json"):
        """
        初始化 OSS 上传工具

        :param config_path: 配置文件路径
        """
        self.config = self._load_config(config_path)
        self.mapping_file = self.config.get("mapping_file", "oss_mapping.json")
        self.mapping_data = self._load_mapping()

    def _load_config(self, config_path: str) -> Dict:
        """加载配置文件"""
        try:
            with open(config_path, "r", encoding="utf-8") as f:
                return json.load(f)
        except FileNotFoundError:
            raise Exception(f"配置文件 {config_path} 不存在")
        except json.JSONDecodeError:
            raise Exception(f"配置文件 {config_path} 格式错误")

    def _load_mapping(self) -> Dict:
        """加载已有的映射文件"""
        if not os.path.exists(self.mapping_file):
            return {}

        try:
            with open(self.mapping_file, "r", encoding="utf-8") as f:
                return json.load(f)
        except json.JSONDecodeError:
            return {}

    def _save_mapping(self):
        """保存映射关系到文件"""
        with open(self.mapping_file, "w", encoding="utf-8") as f:
            json.dump(self.mapping_data, f, indent=2, ensure_ascii=False)

    def find_files(
            self,
            root_dir: str,
            max_size: Optional[int] = None
    ) -> List[str]:
        """
        查找符合条件的文件

        :param root_dir: 根目录
        :param min_size: 最小文件大小(字节)
        :param max_size: 最大文件大小(字节)
        :return: 符合条件的文件路径列表
        """
        valid_files = []

        for root, _, files in os.walk(root_dir):
            for file in files:
                file_path = os.path.join(root, file)
                file_size = os.path.getsize(file_path)
                file_name = os.path.basename(file_path)
                file_ext = os.path.splitext(file_name)[1].lower()
                if "png" in file_ext or "jpg" in file_ext or "jpeg" in file_ext or  "gif" in file_ext:
                    # 检查文件大小是否符合要求
                    size_ok = False
                    if max_size is not None and file_size > max_size:
                        size_ok = True

                    if size_ok:
                        valid_files.append(file_path)

        return valid_files

    def get_oss_upload_params(self , file_path: str) -> Dict:
        """
        从 OSS 接口获取上传参数

        :param file_path: 要上传的文件路径
        :return: 上传参数字典
        """
        file_name = os.path.basename(file_path)
        file_ext = os.path.splitext(file_name)[1].lower()

        # 这里根据实际 OSS 接口调整
        api_url = self.config["oss_api_url"]
        headers = {
            'Content-Type': 'application/json',
            'Authorization': self.config["token"],
            'token': self.config["token"]
        }
        payload = {
            "file": file_name,
        }

        try:
            response = requests.get(
                url=api_url,
                params=payload,
                headers=headers,
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"获取 OSS 上传参数失败: {str(e)}")

    def upload_to_oss(self, file_path: str,  form_data:dict, host:str) -> str:
        """
        上传文件到 OSS
        :param file_path: 本地文件路径
        :return: OSS 文件 URL
        """

        try:
            with open(file_path, "rb") as f:
                files = {"file": (os.path.basename(file_path), f)}
                response = requests.post(
                    host,
                    data=form_data,
                    files=files,
                    timeout=self.config.get("timeout", 30)
                )
                return response.status_code
        except Exception as e:
            raise Exception(f"上传文件 {file_path} 到 OSS 失败: {str(e)}")

    def process_directory(
            self,
            root_dir: str,
            max_size: Optional[int] = None,
            skip_existing: bool = True
    ) -> Dict[str, str]:
        """
            处理目录中的所有文件
            :param root_dir: 根目录
            :param max_size: 最大文件大小(字节)
            :param skip_existing: 是否跳过已上传的文件
            :return: 本地路径到 OSS URL 的映射
        """
        files = self.find_files(root_dir, max_size)
        results = {}

        for file_path in files:
            # 检查是否已经上传过
            if skip_existing and file_path in self.mapping_data:
                print(f"跳过已上传文件: {file_path}")
                results[file_path] = self.mapping_data[file_path]
                continue
            print(f"处理文件: {file_path}")

            try:
                # 1. 获取上传参数
                upload_params = self.get_oss_upload_params(file_path)

                upload_params = upload_params["result"]
                accessid = upload_params['accessid']
                path = upload_params["path"]
                signature = upload_params["signature"]
                expire = upload_params["expire"]
                host = upload_params["host"]
                callback = upload_params["callback"]
                dir = upload_params["dir"]
                policy = upload_params["policy"]

                form_data = {
                    "key": dir,
                    "OSSAccessKeyId": accessid,
                    "policy": policy,
                    "Signature": signature,
                    "success_action_status": 200,
                    "keys": path
                }

                # # 2. 上传文件
                upload_res_code = self.upload_to_oss(file_path, form_data, host)
                if upload_res_code == 200:
                    oss_url = f"{host}/{dir}"

                    self.mapping_data[file_path] = oss_url
                    results[file_path] = oss_url

                    print(f"上传成功: {file_path} -> {oss_url}")
            except Exception as e:
                print(f"处理文件 {file_path} 失败: {str(e)}")

        # 保存映射关系
        self._save_mapping()
